package top.lilele.adminSystem.service;

import cn.hutool.core.util.IdUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.google.common.io.ByteStreams;
import io.minio.GetObjectResponse;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.FastByteArrayOutputStream;
import org.springframework.web.multipart.MultipartFile;
import top.lilele.adminSystem.entity.AdminFileEntity;
import top.lilele.adminSystem.entity.FilePartEntity;
import top.lilele.adminSystem.exception.business.FileNotExistException;
import top.lilele.adminSystem.mapper.AdminFileMapper;
import top.lilele.adminSystem.mapper.FilePartMapper;
import top.lilele.adminSystem.utils.MinioUtil;
import top.lilele.adminSystem.utils.Utils;

import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.net.URLEncoder;


/**
 * @author lilele
 */
@Service
@Slf4j
public class AdminFileService {
    @Resource
    MinioUtil minioUtil;
    @Value("${minio.bucketName}")
    private String bucketName;

    @Resource
    AdminFileMapper adminFileMapper;

    @Resource
    FilePartMapper filePartMapper;

    /**
     * 一次性上传文件
     *
     * @param multipartFile 文件
     * @param md5 文件md5
     * @param isGenerateUrl 是否生成get链接
     * @return
     */
    @SneakyThrows
    public AdminFileEntity uploadFile(MultipartFile multipartFile, String md5,Boolean isGenerateUrl) {
        AdminFileEntity adminFileEntity = Utils.generateFileInfo(multipartFile, bucketName);
        // 文件上传
        minioUtil.uploadStream(multipartFile.getInputStream(), bucketName, adminFileEntity.getFileName());
        // 生成可以访问的连接
        if (isGenerateUrl) {
            String url = minioUtil.generateGetUrl(bucketName, adminFileEntity.getFileName(), 7);
            String s = url.split("\\?")[0];
            adminFileEntity.setUrl(s);
        }
        adminFileEntity.setMd5(md5);
        // 录入数据
        adminFileMapper.insert(adminFileEntity);
        return adminFileEntity;
    }

    /**
     * 下载文件
     *
     * @param fileId              文件唯一id
     * @param httpServletResponse http servlet响应
     */
    @SneakyThrows
    public void download(String fileId, HttpServletResponse httpServletResponse) {
        AdminFileEntity adminFileEntity = adminFileMapper.selectById(fileId);
        if (adminFileEntity == null) {
            throw new FileNotExistException("文件不存在");
        }
        GetObjectResponse response = minioUtil.download(bucketName, adminFileEntity.getFileName());
        // 字节输出流
        FastByteArrayOutputStream os = new FastByteArrayOutputStream();
        // minio输入流拷贝到输出流中，供服容器返回
        ByteStreams.copy(response, os);
        httpServletResponse.setCharacterEncoding("utf-8");
        httpServletResponse.setHeader("Content-Disposition", "attachment;fileName=" + URLEncoder.encode(adminFileEntity.getFileOriginName(), "UTF-8"));
        try (ServletOutputStream stream = httpServletResponse.getOutputStream()) {
            stream.write(os.toByteArray());
            stream.flush();
        }
    }

    /**
     * 分片上传
     *
     * @param generateUploadId 批次id
     * @param chunkTotal       总片数
     * @param currentChunk     当前片数
     * @param md5              前端md5
     * @param multipartFile    文件
     */
    @SneakyThrows
    public void partUpload(String generateUploadId, Integer chunkTotal, Integer currentChunk, String md5, MultipartFile multipartFile) {
        // 获取是否已经有当前的切片信息了
        AdminFileEntity info = adminFileMapper.selectOne(new LambdaQueryWrapper<AdminFileEntity>().eq(AdminFileEntity::getMd5, md5));
        if (info != null) {
            log.info("md5为--{}--已经有切片信息", md5);
            return;
        }
        //当是第一片的时候，创建桶
        if (currentChunk == 0) {
            minioUtil.makeBucket(generateUploadId);
        }
        // minio文件名
        String fileName = IdUtil.fastSimpleUUID();
        // 上传文件
        minioUtil.uploadStream(multipartFile.getInputStream(), fileName, generateUploadId);
        // 构造文件实体
        AdminFileEntity fileInfo = Utils.generateFileInfo(multipartFile, generateUploadId);
        fileInfo.setMd5(md5);
        adminFileMapper.insert(fileInfo);
        // 构造切片信息实体
        FilePartEntity filePartEntity = new FilePartEntity();
        filePartEntity.setUploadId(generateUploadId);
        filePartEntity.setCurrentChunk(currentChunk);
        filePartEntity.setChunkTotal(chunkTotal);
        filePartEntity.setFileId(fileInfo.getId());
        // 录入切片数据
        filePartMapper.insert(filePartEntity);
        //当在文件最后一个元素的时候，合并文件
//        if (currentChunk + 1 == chunkTotal) {
//            mergePartAndUpload(generateUploadId);
//        }
    }

    public AdminFileEntity Md5Exist(String md5) {
        AdminFileEntity info = adminFileMapper.selectOne(new LambdaQueryWrapper<AdminFileEntity>().eq(AdminFileEntity::getMd5, md5));
        return  info;
    }

    /**
     * 根据文件id删除minio文件
     *
     * @param id
     */
    public void deleteFileById(String id) {
        AdminFileEntity info = adminFileMapper.selectById(id);
        log.info("文件信息{}", info);
        // 删除对象信息
        minioUtil.deleteObj(info.getBucketName(), info.getFileName());
        // 删除存储信息
        adminFileMapper.deleteById(id);
    }
}
